#ifndef ALU_H
#define ALU_H

#include "channel.h"
#include "node.h"
#include "port.h"
#include "lane.h"
#include <complex>

#include "ip/opdef.h"

typedef struct LaneChannelAluSrcPack
{
    int addr;
    std::complex<float> data0;
    std::complex<float> data1;
    int op;
} LaneChannelAluSrcPack;

typedef struct LaneChannelAluDstPack
{
    int addr;
    std::complex<float> data;
} LaneChannelAluDstPack;

class LaneChannelAluSrcReceiver : public LanePortReceiver
{
public:
    PortIn<int> * addr;
    PortIn<std::complex<float>> * data0;
    PortIn<std::complex<float>> * data1;
    PortIn<int> * op;

    LaneChannelAluSrcReceiver(JObject *parent = nullptr);
    LaneChannelAluSrcPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelAluDstReceiver : public LanePortReceiver
{
public:
    PortIn<int> * addr;
    PortIn<std::complex<float>> * data;

    LaneChannelAluDstReceiver(JObject *parent = nullptr);
    LaneChannelAluDstPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelAluSrcSender : public LanePortSender
{
public:
    PortOut<int> * addr;
    PortOut<std::complex<float>> * data0;
    PortOut<std::complex<float>> * data1;
    PortOut<int> * op;

    LaneChannelAluSrcSender(JObject *parent = nullptr);
    void connect(LaneChannelAluSrcReceiver *sender);
    void put(int data_addr, std::complex<float> data_data0, std::complex<float> data_data1, int data_op);
    void put_bypass(LaneChannelAluSrcReceiver *receiver);
    bool is_any_full();
};

class LaneChannelAluDstSender : public LanePortSender
{
public:
    PortOut<int> * addr;
    PortOut<std::complex<float>> * data;

    LaneChannelAluDstSender(JObject *parent = nullptr);
    void connect(LaneChannelAluDstReceiver *sender);
    void put(int data_addr, std::complex<float> data_data);
    void put_bypass(LaneChannelAluDstReceiver *receiver);
    bool is_any_full();
};

class ChannelAluReceiver : public ChannelPortReceiver
{
public:
    LaneChannelAluSrcReceiver * Src;
    LaneChannelAluDstSender * Dst;

    ChannelAluReceiver(JObject *parent);
};

class ChannelAluSender : public ChannelPortSender
{
public:
    LaneChannelAluSrcSender * Src;
    LaneChannelAluDstReceiver * Dst;

    ChannelAluSender(JObject *parent);
};

class ChannelAlu : public Channel
{
public:
    static void connect(ChannelAluSender *sender, ChannelAluReceiver *receiver);
};

class NodeAlu : public Node
{
public:
    int reg_dst_addr;
    std::complex<float> reg_src_0;
    std::complex<float> reg_src_1;
    std::complex<float> reg_dst_data;
    int reg_op_code;
    ChannelAluReceiver * port_receiver;

    NodeAlu(Node *parent);
    NodeTaskList tasks() const override;

    void task_update();
};

#endif // ALU_H